package com.softserveinc.moviedbapp.customview;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.softserveinc.moviedbapp.R;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class MovieTheaterView extends View {

	private static final String TAG = MovieTheaterView.class.getSimpleName();
	private static final String SELECTED_X = "selectedX";
	private static final String SELECTED_Y = "selectedY";
	private static final String VIEW_STATE = "viewState";
	private static final String CURRENT_PRICE = "current_price";
	private static final String CURRENT_SELECTED_SEATS_MAP_SIZE = "map_size";
	private static final String CURRENT_POSITION = "current_position";
	private static final String CURRENT_POSITION_VALUE = "current_position_value";
	private static double sSeatPrice = 20;

	private int selectedX;
	private int selectedY;
	private int mRows;
	private int mSeatsInRow;

	private float mCellWidth;
	private float mCellHeight;

	private double mCurrentPrice;

	private Bitmap mSeatEmptyBitmap;
	private Bitmap mSeatOccupiedBitmap;
	private Bitmap mSeatSelectedBitmap;

	private Resources mResources;
	private OnDrawingViewListener mListener;

	private HashMap<Integer, Boolean> mBookingMap;
	private HashMap<Integer, Boolean> mCurrentSessionBookingMap;

	private final Rect selectedRect = new Rect();
	private float mActionDownXCoordinate;
	private float mActionDownYCoordinate;
	private boolean mHandledMoveEvent;
	private Paint mHighlightPaint;
	private Paint mForegroundPaint;
	private Paint mRedGridLinePaint;

	public MovieTheaterView(final Context context) {
		super(context, null);
		Log.d(TAG, "DrawingView(1) constructor called and ended");
	}

	public MovieTheaterView(final Context context, final AttributeSet attrs) {
		super(context, attrs, 0);
		Log.d(TAG, "DrawingView(1, 2) constructor called");

		if (attrs != null) {
			Log.d(TAG, "AttributeSet isn't null");
		} else {
			Log.d(TAG, "AttributeSet is null");
		}

		TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MovieTheaterView);

		mSeatsInRow = typedArray.getInt(R.styleable.MovieTheaterView_seatsInRow, 5);
		mRows = typedArray.getInt(R.styleable.MovieTheaterView_rows, 5);

		typedArray.recycle();

		setFocusable(true);
		setFocusableInTouchMode(true);

		// try {
		mListener = (MovieTheaterBookingActivity) context;
		// } catch ( ClassCastException e ) {
		// throw new ClassCastException( context.toString() + " must implement OnDrawingViewListener");
		// }

		mResources = context.getResources();

		mSeatEmptyBitmap = BitmapFactory.decodeResource(mResources, R.drawable.theater_seat_empty);
		mSeatOccupiedBitmap = BitmapFactory.decodeResource(mResources, R.drawable.theater_seat_occupied);
		mSeatSelectedBitmap = BitmapFactory.decodeResource(mResources, R.drawable.theater_seat_selected);

		mBookingMap = mListener.onSeatsUpdate(getSeatsCount());

		initCurrentSessionBookingMap();
		initPaints();
		Log.d(TAG, "DrawingView(1, 2) constructor ending");
	}

	public MovieTheaterView(final Context context, final AttributeSet attrs, final int defStyle) {
		super(context, attrs, defStyle);
		Log.d(TAG, "DrawingView(1, 2, 3) constructor called & ended");
	}

	private void initCurrentSessionBookingMap() {
		Iterator<Integer> iterator = mBookingMap.keySet().iterator();
		mCurrentSessionBookingMap = new HashMap<Integer, Boolean>();
		int counter = 0;

		while (iterator.hasNext()) {
			Integer key = iterator.next();
			if (mBookingMap.get(key) == false) {
				mCurrentSessionBookingMap.put(counter, false);
			}
			counter++;
		}
	}

	private void initPaints() {
		mRedGridLinePaint = new Paint();
		mRedGridLinePaint.setColor(Color.RED);

		mHighlightPaint = new Paint();
		mHighlightPaint.setColor(Color.parseColor("#E8D08E"));
		mHighlightPaint.setStyle(Style.FILL);
		// mHighlightPaint.setStrokeWidth(7);
	}

	@Override
	protected Parcelable onSaveInstanceState() {
		Parcelable parcelable = super.onSaveInstanceState();
		Log.d(TAG, "in onSaveInstanceState()");
		Bundle bundle = new Bundle();
		bundle.putInt(SELECTED_X, selectedX);
		bundle.putInt(SELECTED_Y, selectedY);

		bundle.putInt(CURRENT_SELECTED_SEATS_MAP_SIZE, mCurrentSessionBookingMap.size());
		Set<Integer> keySet = mCurrentSessionBookingMap.keySet();

		Iterator<Integer> iterator = keySet.iterator();
		int counter = 0;
		while (iterator.hasNext()) {
			Integer key = iterator.next();
			bundle.putInt(CURRENT_POSITION + counter, key);
			bundle.putBoolean(CURRENT_POSITION_VALUE + counter, mCurrentSessionBookingMap.get(key));
			counter++;
		}

		bundle.putDouble(CURRENT_PRICE, mCurrentPrice);
		bundle.putParcelable(VIEW_STATE, parcelable);
		Log.d(TAG, "in onSaveInstanceState() selectedX = " + selectedX + " selectedY = " + selectedY);

		return bundle;
	}

	@Override
	protected void onRestoreInstanceState(final Parcelable state) {
		Log.d(TAG, "in onRestoreInstanceState()");
		Bundle bundle = (Bundle) state;

		int mapSize = bundle.getInt(CURRENT_SELECTED_SEATS_MAP_SIZE);
		mCurrentSessionBookingMap = new HashMap<Integer, Boolean>();
		int currentPosition;
		boolean currentPositionValue;
		for (int i = 0; i < mapSize; i++) {
			currentPosition = bundle.getInt(CURRENT_POSITION + i);
			currentPositionValue = bundle.getBoolean(CURRENT_POSITION_VALUE + i);
			Log.d(TAG, "in onRestoreInstanceState() currentPosition = " + currentPosition + " currentPositionValue = "
					+ currentPositionValue);
			mCurrentSessionBookingMap.put(currentPosition, currentPositionValue);
		}
		highlightRectangle(bundle.getInt(SELECTED_X), bundle.getInt(SELECTED_Y), false);
		mCurrentPrice = bundle.getDouble(CURRENT_PRICE);
		mListener.onPriceChanged(mCurrentPrice);

		super.onRestoreInstanceState(bundle.getParcelable(VIEW_STATE));
	}

	@Override
	protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) {
		Log.d(TAG, "onSizeChanged() called");
		Log.d(TAG, "w = " + w + " h = " + h + " oldw = " + oldw + " oldh = " + oldh);

		mCellWidth = w / (float) mSeatsInRow;
		mCellHeight = h / (float) mRows;

		Log.d(TAG, "mCellWidth = " + mCellWidth + " mCellHeight = " + mCellHeight);

		setRectCoordinates(selectedX, selectedY, selectedRect);

		super.onSizeChanged(w, h, oldw, oldh);
	}

	@Override
	protected void onDraw(final Canvas canvas) {
		Log.d(TAG, "in onDraw()");
		int canvasWidth = canvas.getWidth();
		int canvasHeight = canvas.getHeight();

		Log.d(TAG, "canvasWidth = " + canvasWidth + " canvasHeight = " + canvasHeight);

		int viewWidth = getWidth();
		int viewHeight = getHeight();

		Log.d(TAG, "viewWidth = " + viewWidth + " viewHeight = " + viewHeight);

		for (int i = 0; i < mRows; i++) {
			canvas.drawLine(0, mCellHeight * (i + 1), viewWidth, mCellHeight * (i + 1), mRedGridLinePaint);
			canvas.drawLine(mCellWidth * (i + 1), 0, mCellWidth * (i + 1), viewHeight, mRedGridLinePaint);
		}

		/* Draw the selection... */
		Log.d(TAG, "selectedRect = " + selectedRect);

		canvas.drawRect(selectedRect, mHighlightPaint);

		mForegroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mForegroundPaint.setColor(Color.BLACK);
		mForegroundPaint.setStyle(Style.FILL);
		mForegroundPaint.setTextSize(mCellHeight * 0.35f);
		mForegroundPaint.setTextScaleX(mCellWidth / mCellHeight);
		mForegroundPaint.setTextAlign(Paint.Align.CENTER);

		/* Draw the number in the center of the cell */
		FontMetrics fontMetrics = mForegroundPaint.getFontMetrics();
		/* Centering in X: use alignment (and X at midpoint) */
		float xCenter = mCellWidth / 2;
		/* Centering in Y: measure ascent/descent first */
		float yCenter = mCellHeight / 2 - (fontMetrics.ascent + fontMetrics.descent) / 2;

		Log.d(TAG, "xCenter = " + xCenter + " yCenter = " + yCenter);

		/* bitmaps left/top coordinates */
		float seatLeft = xCenter - mSeatEmptyBitmap.getWidth() / 2;
		float seatTop = mCellHeight / 2 - mSeatEmptyBitmap.getHeight() / 2;

		Log.d(TAG, "seatLeft = " + seatLeft + " seatTop = " + seatTop);
		int seatCounter = 0;
		for (int i = 0; i < mRows; i++) {
			Boolean isCurrentSessionSeatBooked;
			for (int j = 0; j < mSeatsInRow; j++) {

				isCurrentSessionSeatBooked = mCurrentSessionBookingMap.get(seatCounter);

				if (isCurrentSessionSeatBooked == null) {
					canvas.drawBitmap(mSeatOccupiedBitmap, seatLeft + (j * mCellWidth), seatTop + (i * mCellHeight),
							null);
				} else if (isCurrentSessionSeatBooked) {
					canvas.drawBitmap(mSeatSelectedBitmap, seatLeft + (j * mCellWidth), seatTop + (i * mCellHeight),
							null);
				} else {
					canvas.drawBitmap(mSeatEmptyBitmap, seatLeft + (j * mCellWidth), seatTop + (i * mCellHeight), null);
				}

				canvas.drawText(String.valueOf(seatCounter + 1), xCenter + (j * mCellWidth), yCenter
						+ (i * mCellHeight), mForegroundPaint);
				seatCounter++;
			}
		}
	}

	@Override
	public boolean onTouchEvent(final MotionEvent event) {
		Log.d(TAG, "in onTouchEvent()");
		String action = "";
		int actionInt = event.getAction();
		if (actionInt == MotionEvent.ACTION_DOWN) {
			action = "ACTION_DOWN";
		} else if (actionInt == MotionEvent.ACTION_UP) {
			action = "ACTION_UP";
		} else if (actionInt == MotionEvent.ACTION_MOVE) {
			action = "ACTION_MOVE";
		} else if (actionInt == MotionEvent.ACTION_POINTER_DOWN) {
			action = "ACTION_POINTER_DOWN";
		} else if (actionInt == MotionEvent.ACTION_POINTER_UP) {
			action = "ACTION_POINTER_UP";
		} else if (actionInt == MotionEvent.ACTION_CANCEL) {
			action = "ACTION_CANCEL";
		}

		Log.d(TAG, "in onTouchEvent() event is " + action);

		if (actionInt == MotionEvent.ACTION_DOWN) {

			mHandledMoveEvent = false;

			mActionDownXCoordinate = event.getX() / mCellWidth;
			mActionDownYCoordinate = event.getY() / mCellHeight;

			highlightRectangle((int) mActionDownXCoordinate, (int) mActionDownYCoordinate, true);

			Log.d(TAG, "onTouchEvent() action is " + action + "    event.getX() = " + mActionDownXCoordinate
					+ " event.getY() = " + mActionDownYCoordinate);
			return true;
		} else if (actionInt == MotionEvent.ACTION_MOVE) {
			Log.d(TAG, "mHandledMoveEvent " + mHandledMoveEvent);

			if ((int) mActionDownXCoordinate != (int) (event.getX() / mCellWidth)
					|| (int) mActionDownYCoordinate != (int) (event.getY() / mCellHeight)) {

				if (!mHandledMoveEvent) {
					highlightRectangle((int) (mActionDownXCoordinate), (int) (mActionDownYCoordinate), true);
				}
				mHandledMoveEvent = true;
			} else {
				Log.d(TAG, "mHandledMoveEvent " + mHandledMoveEvent);
				if (mHandledMoveEvent) {
					highlightRectangle((int) (mActionDownXCoordinate), (int) (mActionDownYCoordinate), true);

				}
				mHandledMoveEvent = false;

			}
		}
		return super.onTouchEvent(event);

	}

	/* highlight the selected rectangle */
	private void highlightRectangle(final int x, final int y, final boolean changeSeat) {
		Log.d(TAG, "in highlightRectangle()");

		/* if not call invalidate() previous rectangle won't disappear */
		invalidate(selectedRect);
		selectedX = Math.min(Math.max(x, 0), mSeatsInRow - 1);
		selectedY = Math.min(Math.max(y, 0), mRows - 1);
		setRectCoordinates(selectedX, selectedY, selectedRect);

		/* if it is not called from onRestoreInstanceState, than change seat selection */
		if (changeSeat) {
			int currentMapIndex = selectedX + selectedY * mSeatsInRow;
			Boolean isCurrentSeatBooked = mCurrentSessionBookingMap.get(currentMapIndex);
			if (isCurrentSeatBooked != null) {
				if (!isCurrentSeatBooked) {
					mCurrentSessionBookingMap.put(currentMapIndex, true);
					mCurrentPrice += calculateSeatPrice(selectedX);
					mListener.onPriceChanged(mCurrentPrice);
				} else {
					mCurrentSessionBookingMap.put(currentMapIndex, false);
					mCurrentPrice -= calculateSeatPrice(selectedX);
					mListener.onPriceChanged(mCurrentPrice);
				}
			}

		}

		Log.d(TAG, "highlightRectangle() selectedX = " + selectedX + " selectedY = " + selectedY);

		invalidate(selectedRect);
	}

	private double calculateSeatPrice(final int selectedX) {

		if (mSeatsInRow % 3 == 0) {
			if (selectedX < mSeatsInRow / 3 || selectedX >= mSeatsInRow / 3 * 2) {
				return sSeatPrice;
			} else {
				return sSeatPrice * 1.2;
			}
		} else {
			if (selectedX < mSeatsInRow / 3 || selectedX >= mSeatsInRow / 3 * 3) {
				return sSeatPrice;
			} else {
				return sSeatPrice * 1.2;
			}
		}

	}

	/* make the rectangle to occupy x, y cell */
	private void setRectCoordinates(final int x, final int y, final Rect rect) {
		Log.d(TAG, "in setRectCoordinates()");
		rect.set((int) (x * mCellWidth), (int) (y * mCellHeight), (int) (x * mCellWidth + mCellWidth), (int) (y
				* mCellHeight + mCellHeight));
	}

	public HashMap<Integer, Boolean> getBookingMap() {
		return mBookingMap;
	}

	public void setBookingMap(final HashMap<Integer, Boolean> mBookingMap) {
		this.mBookingMap = mBookingMap;
	}

	public HashMap<Integer, Boolean> getCurrentSessionBookingMap() {
		return mCurrentSessionBookingMap;
	}

	public int getSeatsCount() {
		return mRows * mSeatsInRow;
	}

	public interface OnDrawingViewListener {

		public HashMap<Integer, Boolean> onSeatsUpdate(int seatCount);

		public void onPriceChanged(double price);

	}

}
